package com.mofang.ticketing.user.serve.project.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.mofang.ticketing.common.core.dto.AreaDTO;
import com.mofang.ticketing.common.core.dto.PrimaryIdDTO;
import com.mofang.ticketing.common.core.enums.account.BelongEnum;
import com.mofang.ticketing.common.core.enums.account.RoleTypeEnum;
import com.mofang.ticketing.common.core.enums.project.ProjectTypeEnum;
import com.mofang.ticketing.common.core.exception.BizException;
import com.mofang.ticketing.common.core.util.CopyUtil;
import com.mofang.ticketing.common.security.util.SecurityUtil;
import com.mofang.ticketing.user.mapping.entity.MerchantInfoDO;
import com.mofang.ticketing.user.mapping.entity.ProjectInfoDO;
import com.mofang.ticketing.user.mapping.entity.ProjectUserDO;
import com.mofang.ticketing.user.mapping.entity.SysUserRoleDO;
import com.mofang.ticketing.user.mapping.mapper.*;
import com.mofang.ticketing.user.serve.project.dto.request.*;
import com.mofang.ticketing.user.serve.project.dto.response.ProjectDetailRespDTO;
import com.mofang.ticketing.user.serve.project.dto.response.ProjectListPageRespDTO;
import com.mofang.ticketing.user.serve.project.dto.response.ProjectSelectRespDTO;
import com.mofang.ticketing.user.serve.project.dto.response.ProjectUserListPageRespDTO;
import com.mofang.ticketing.user.serve.project.mapper.ProjectMapper;
import com.mofang.ticketing.user.serve.project.service.ProjectService;
import com.mofang.ticketing.user.serve.role.mapper.RoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author LGC
 * @date 2023/3/15 17:18
 */
@Slf4j
@Service
public class ProjectServiceImpl implements ProjectService {

    @Resource
    private ProjectMapper projectMapper;
    @Resource
    private ProjectInfoDOMapper projectInfoDOMapper;
    @Resource
    private SysUserRoleDOMapper sysUserRoleDOMapper;
    @Resource
    private ProjectUserDOMapper projectUserDOMapper;
    @Resource
    private MerchantInfoDOMapper merchantInfoDOMapper;
    @Resource
    private SysUserDOMapper sysUserDOMapper;
    @Resource
    private RoleMapper roleMapper;

    @Override
    public ProjectDetailRespDTO projectDetail(PrimaryIdDTO dto) {
        ProjectInfoDO projectInfoDO = projectInfoDOMapper.selectById(dto.getId());
        if (projectInfoDO == null) {
            throw new BizException("项目不存在");
        }
        return CopyUtil.copy(projectInfoDO, ProjectDetailRespDTO.class);
    }

    @Override
    public IPage<ProjectListPageRespDTO> projectListPage(ProjectListPageReqDTO dto) {
        return projectMapper.projectListPage(dto.getPage().getPage(), dto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void projectAdd(ProjectAddReqDTO dto) {
        Integer projectType = dto.getProjectType();
        if (projectType.equals(ProjectTypeEnum.MERCHANT_TYPE.getKey())) {
            if (StrUtil.isBlank(dto.getMerchantJson())) {
                throw new BizException("商户不能为空");
            }
            List<Long> merchants = JSONUtil.toList(dto.getMerchantJson(), Long.class);
            if (merchants.size() <= 0) {
                throw new BizException("商户不能为空");
            }
        }
        if (projectType.equals(ProjectTypeEnum.AREA_TYPE.getKey())) {
            if (StrUtil.isBlank(dto.getAreaJson())) {
                throw new BizException("地区不能为空");
            }
            List<AreaDTO> areas = JSONUtil.toList(dto.getAreaJson(), AreaDTO.class);
            if (areas.size() <= 0) {
                throw new BizException("地区不能为空");
            }
        }
        Long userId = SecurityUtil.getUserId();
        ProjectInfoDO insertProject = CopyUtil.copy(dto, ProjectInfoDO.class);
        insertProject.setCreateUserId(userId);
        insertProject.setCreateTime(new Date());
        insertProject.setUpdateUserId(userId);
        insertProject.setUpdateTime(new Date());
        insertProject.setRevision(0);
        projectInfoDOMapper.insert(insertProject);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void projectUpdate(ProjectUpdateReqDTO dto) {
        ProjectInfoDO oldProject = projectInfoDOMapper.selectById(dto.getId());
        if (oldProject == null) {
            throw new BizException("项目不存在");
        }
        Long projectId = oldProject.getId();
        Integer projectType = dto.getProjectType();
        if (projectType.equals(ProjectTypeEnum.MERCHANT_TYPE.getKey())) {
            if (StrUtil.isBlank(dto.getMerchantJson())) {
                throw new BizException("商户不能为空");
            }
            List<Long> merchants = JSONUtil.toList(dto.getMerchantJson(), Long.class);
            if (merchants.size() <= 0) {
                throw new BizException("商户不能为空");
            }
            if (oldProject.getProjectType().equals(ProjectTypeEnum.AREA_TYPE.getKey())) {
                log.info("项目类型由:{} 变成:{}", ProjectTypeEnum.AREA_TYPE.getValue(), ProjectTypeEnum.MERCHANT_TYPE.getValue());
            }
        }
        if (projectType.equals(ProjectTypeEnum.AREA_TYPE.getKey())) {
            if (StrUtil.isBlank(dto.getAreaJson())) {
                throw new BizException("地区不能为空");
            }
            List<AreaDTO> areas = JSONUtil.toList(dto.getAreaJson(), AreaDTO.class);
            if (areas.size() <= 0) {
                throw new BizException("地区不能为空");
            }
            if (oldProject.getProjectType().equals(ProjectTypeEnum.MERCHANT_TYPE.getKey())) {
                log.info("项目类型由:{} 变成:{}", ProjectTypeEnum.MERCHANT_TYPE.getValue(), ProjectTypeEnum.AREA_TYPE.getValue());
            }
        }

        ProjectInfoDO updateProject = CopyUtil.copy(dto, ProjectInfoDO.class);
        // 项目类型发生改变,商户改变，地区改变，清除项目用户绑定关系，重新建立绑定关系
        if (!oldProject.getProjectType().equals(projectType) ||
                !dto.getMerchantJson().equals(oldProject.getMerchantJson()) ||
                !dto.getAreaJson().equals(oldProject.getAreaJson())) {
            List<ProjectUserDO> oldProjectUsers = projectMapper.selectProjectUserList(projectId);
            if (CollectionUtil.isNotEmpty(oldProjectUsers)) {
                Set<Long> oldUserSet = oldProjectUsers.stream().map(ProjectUserDO::getUserId).collect(Collectors.toSet());
                removeProjectUserRole(oldUserSet);
                Set<Long> merchantIds = getMerchantIdSetByProject(updateProject);
                for (Long userId : oldUserSet) {
                    insetProjectUserRole(userId, merchantIds, projectId);
                }
            }
        }
        // 更新项目信息
        updateProject.setUpdateUserId(SecurityUtil.getUserId());
        updateProject.setUpdateTime(new Date());
        LambdaUpdateWrapper<ProjectInfoDO> updateWrapper = new LambdaUpdateWrapper<ProjectInfoDO>()
                .set(ProjectInfoDO::getRevision, oldProject.getRevision() + 1)
                .eq(ProjectInfoDO::getId, projectId)
                .eq(ProjectInfoDO::getRevision, oldProject.getRevision());
        int update = projectInfoDOMapper.update(updateProject, updateWrapper);
        if (update <= 0) {
            throw new BizException("更新项目失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void projectDel(PrimaryIdDTO dto) {
        ProjectInfoDO projectInfoDO = projectInfoDOMapper.selectById(dto.getId());
        if (projectInfoDO == null) {
            throw new BizException("项目不存在");
        }
        List<ProjectUserDO> oldProjectUsers = projectMapper.selectProjectUserList(projectInfoDO.getId());
        if (CollectionUtil.isNotEmpty(oldProjectUsers)) {
            Set<Long> oldUserSet = oldProjectUsers.stream().map(ProjectUserDO::getUserId).collect(Collectors.toSet());
            removeProjectUserRole(oldUserSet);
            // 删除用户
            sysUserDOMapper.deleteBatchIds(oldUserSet);
        }
        projectInfoDOMapper.deleteById(projectInfoDO.getId());
    }


    @Override
    public List<ProjectSelectRespDTO> projectSelect() {
        return projectMapper.projectSelect();
    }

    @Override
    public IPage<ProjectUserListPageRespDTO> projectUserListPage(ProjectUserListPageReqDTO dto) {
        return projectMapper.projectUserListPage(dto.getPage().getPage(), dto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void projectUserDel(ProjectUserDelReqDTO dto) {
        ProjectInfoDO projectInfoDO = projectInfoDOMapper.selectById(dto.getProjectId());
        if (projectInfoDO == null) {
            throw new BizException("项目不存在");
        }
        List<ProjectUserDO> oldProjectUsers = projectMapper.selectProjectUserList(projectInfoDO.getId());
        if (CollectionUtil.isNotEmpty(oldProjectUsers)) {
            Set<Long> oldUserSet = oldProjectUsers.stream().map(ProjectUserDO::getUserId).collect(Collectors.toSet());
            if (!oldUserSet.contains(dto.getUserId())) {
                throw new BizException("该用户不属于该项目");
            }
            Set<Long> delUserSet = new HashSet<>(1);
            delUserSet.add(dto.getUserId());
            removeProjectUserRole(delUserSet);
            sysUserDOMapper.deleteBatchIds(delUserSet);
        }
    }


    @Override
    public Set<Long> getMerchantIdSetByProject(ProjectInfoDO projectInfoDO) {
        Set<Long> merchantIdSet = new HashSet<>();
        Integer projectType = projectInfoDO.getProjectType();
        if (projectType.equals(ProjectTypeEnum.MERCHANT_TYPE.getKey())) {
            String merchantJson = projectInfoDO.getMerchantJson();
            merchantIdSet.addAll(JSONUtil.toList(merchantJson, Long.class));
        }
        if (projectType.equals(ProjectTypeEnum.AREA_TYPE.getKey())) {
            String areaJson = projectInfoDO.getAreaJson();
            List<AreaDTO> areas = JSONUtil.toList(areaJson, AreaDTO.class);
            if (CollectionUtils.isNotEmpty(areas)) {
                for (AreaDTO area : areas) {
                    merchantIdSet.addAll(new LambdaQueryChainWrapper<>(merchantInfoDOMapper)
                            .eq(StrUtil.isNotBlank(area.getProvinceCode()), MerchantInfoDO::getProvinceCode, area.getProvinceCode())
                            .eq(StrUtil.isNotBlank(area.getCityCode()), MerchantInfoDO::getCityCode, area.getCityCode())
                            .eq(StrUtil.isNotBlank(area.getCountyCode()), MerchantInfoDO::getCountyCode, area.getCountyCode())
                            .list().stream().map(MerchantInfoDO::getId).collect(Collectors.toSet()));
                }
            }
        }
        return merchantIdSet;
    }

    @Override
    public void insetProjectUserRole(Long userId, Set<Long> merchantIdSet, Long projectId) {
        for (Long merchantId : merchantIdSet) {
            sysUserRoleDOMapper.insert(new SysUserRoleDO(BelongEnum.PROJECT_MEMBER.getKey(), merchantId,
                    userId, roleMapper.selectRoleByRoleCode(RoleTypeEnum.PROJECT_MEMBER.getRoleCode()).getId()));
            projectUserDOMapper.insert(new ProjectUserDO(projectId, merchantId, userId, new Date()));
        }
    }

    @Override
    public void removeProjectUserRole(Set<Long> userIdSet) {
        if (CollectionUtil.isNotEmpty(userIdSet)) {
            sysUserRoleDOMapper.delete(new LambdaQueryWrapper<SysUserRoleDO>()
                    .eq(SysUserRoleDO::getBelong, BelongEnum.PROJECT_MEMBER.getKey())
                    .in(SysUserRoleDO::getUserId, userIdSet));
            projectUserDOMapper.delete(new LambdaQueryWrapper<ProjectUserDO>()
                    .in(ProjectUserDO::getUserId, userIdSet));
        }
    }


}
